﻿#region Using References

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

#endregion

namespace Beaker.OpenCube.Game.Blocks
{
	public static class BlockFactory
	{
		private static readonly Dictionary<Block, Type> FactoryMapping
			= new Dictionary<Block, Type>
			{
				{ Block.Air, typeof(AirBlock) }
			};

		/// <summary>
		/// Create a new block of the given type
		/// </summary>
		/// <param name="chunk">The actual chunk this block lives in</param>
		/// <param name="y">Y Coordinate in the chunk - Up, Down</param>
		/// <param name="z">Z Coordinate in the chunk - South, North</param>
		/// <param name="x">X Coordinate in the chunk - East, West</param>
		/// <returns></returns>
		public static WorldBlock Create( Chunk chunk, byte y, byte z, byte x )
		{
			// we start by creating a basic block
			var worldBlock = new WorldBlock( chunk, y, z, x );

			// we let the basic block retrieve all the basic block data
			Block block = worldBlock.GetBasicBlockData();

			// now we check if there is a mapping for this block
			Type type;
			if( FactoryMapping.TryGetValue( block, out type ) )
			{
				// there is, so we get the constructor with the correct parameters
				ConstructorInfo constructor =
					(
						from c in type.GetConstructors( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic )
						let p = c.GetParameters()
						where p.Length == 4
								&& p[0].ParameterType == typeof( Chunk )
								&& p[1].ParameterType == typeof( byte )
								&& p[2].ParameterType == typeof( byte )
								&& p[3].ParameterType == typeof( byte )
						select c
					).Single();

				// invoke to create an instance of that block
				// and return the new block
				return (WorldBlock)constructor.Invoke( new object[] { chunk, y, z, x } );
			}

			// not found in mapping, return the initialy created basic block
			return worldBlock;
		}
	}
}
